<?

//
// +---------------------------------------------------------------------------+
// | Facebook Development Platform (beta) PHP5 client                          |   
// +---------------------------------------------------------------------------+
// | Copyright (c) 2006 Facebook, Inc.                                         | 
// | All rights reserved.                                                      |
// |                                                                           |
// | Redistribution and use in source and binary forms, with or without        |
// | modification, are permitted provided that the following conditions        |
// | are met:                                                                  |
// |                                                                           |
// | 1. Redistributions of source code must retain the above copyright         |
// |    notice, this list of conditions and the following disclaimer.          |
// | 2. Redistributions in binary form must reproduce the above copyright      |
// |    notice, this list of conditions and the following disclaimer in the    |
// |    documentation and/or other materials provided with the distribution.   |
// |                                                                           |
// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR      |
// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.   |
// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  |
// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  |
// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         |
// +---------------------------------------------------------------------------+
// | For help with this library, contact api-help@facebook.com                 |
// +---------------------------------------------------------------------------+
//

/**
 * Facebook Development Platform REST-based API client.
 */
class FacebookRestClient {
  var $secret;
  var $session_key;
  var $api_key;
  var $server_addr;
  var $throw_errors;
  var $last_call_success;
  var $last_error;
  var $debug;

  /**
   * Create the client.
   * @param string $session_key if you haven't gotten a session key yet, leave 
   *                            this as null and then set it later by just 
   *                            directly accessing the $session_key member 
   *                            variable.
   * @param bool   $desktop     set to true if you are a desktop client
   */
  function FacebookRestClient($api_key, $secret, $session_key=null, $throw_errors=true, $debug=false) {
    $this->secret       = $secret;
    $this->session_key  = $session_key;
    $this->api_key      = $api_key;
    $this->throw_errors = $throw_errors;
    $this->debug        = $debug;
    $this->last_call_success = true;
    $this->last_error   = null;
    $this->server_addr  = 'http://api.facebook.com/restserver.php';
  }

  /**
   * Retrieves the events of the currently logged in user between the provided
   * UTC times
   * @param int $start_time UTC lower bound
   * @param int $end_time UTC upper_bound
   * @return array of friends
   */
  function events_getInWindow($start_time, $end_time) {
    return $this->call_method('facebook.events.getInWindow',
                              array('start_time' => $start_time, 'end_time' => $end_time));
  }

  /**
   * Retrieves the friends of the currently logged in user.
   * @return array of friends
   */
  function friends_get() {
    return $this->call_method('facebook.friends.get', array());
  }
  
  /**
   * Retrieves the friends of the currently logged in user,filtered by socialmap type
   * @param string link_type type denoted by string, e.g. "Coworkers"
   * @return array of friends
   */
  function friends_getTyped($link_type) {
    return $this->call_method('facebook.friends.getTyped', array('link_type' => $link_type));
  }
  
  /**
   * Retrieves the requested info fields for the requested set of user.
   * @param array $users_arr an array of user ids to get info for
   * @param array $field_array an array of strings describing the info fields desired
   * @return an array of arrays of info fields, which may themselves be arrays :)
   */
  function users_getInfo($users_arr, $field_array) {
    return $this->call_method('facebook.users.getInfo', array('users' => $users_arr, 'fields' => $field_array));
  }

  /**
   * Retrieves the photos that have been tagged as having the given user.
   * @param string $id  the id of the user in the photos.
   * @param int    $max the number of photos to get (if 0 returns all of them)
   * @returns an array of photo objects.
   */
  function photos_getOfUser($id, $max=0) {
    return $this->call_method('facebook.photos.getOfUser', array('id'=>$id, 'max'=>$max));
  }

  /**
   * Retrieves the albums created by the given user.
   * @param string $id the id of the user whose albums you want.
   * @returns an array of album objects.
   */
  function photos_getAlbums($id) {
    return $this->call_method('facebook.photos.getAlbums', array('id'=>$id));
  }

  /**
   * Retrieves the photos in a given album.
   * @param string $aid the id of the album you want, as returned by photos_getAlbums.
   * @param string $uid the id of the user whose albums you want.
   * @returns an array of photo objects.
   */
  function photos_getFromAlbum($aid, $uid) {
    return $this->call_method('facebook.photos.getFromAlbum', array('aid'=>$aid, 'id'=>$uid));
  }

  /**
   * Retrieves the counts of unread and total messages for the current user.
   * @return an associative array with keys 'unread' and 'total'
   */
  function messages_getCount() {
    return $this->call_method('facebook.messages.getCount', array());
  }

  /**
   * Retrieves the number of wall posts for the specified user.
   * @param string $id the id of the user to get the wall count for (leave 
   *                   blank for the count of the current user).
   * @return an int representing the number of posts
   */
  function wall_getCount($id='') {
    return $this->call_method('facebook.wall.getCount', array('id'=>$id));
  }

  /**
   * Retrieves the number of comments on the current user's photos.
   * @return an int representing the number of comments
   */
  function photos_getCommentCount() {
    return $this->call_method('facebook.photos.getCommentCount', array());
  }

  /**
   * Retrieves the number of pokes (unseen and total) for the current user.
   * @return an associative array with keys 'unseen' and 'total'
   */
  function pokes_getCount() {
    return $this->call_method('facebook.pokes.getCount', array());
  }

  /**
   * Retrieves whether or not two users are friends (note: this is reflexive, 
   * the params can be swapped with no effect).
   * @param array $id1: array of ids of some length X
   * @param array $id2: array of ids of SAME length X
   * @return array of elements in {0,1} of length X, indicating whether each pair are friends
   */
  function friends_areFriends($id1, $id2) {
    return $this->call_method('facebook.friends.areFriends', array('id1'=>$id1, 'id2'=>$id2));
  }

  /**
   * Call this function to retrieve the session information after your user has 
   * logged in.  It will automatically update the client's session_key.
   * @param string $auth_token the token returned by auth_createToken or passed back to your callback_url.
   */
  function auth_getSession($auth_token) {
    $result = $this->call_method('facebook.auth.getSession', array('auth_token'=>$auth_token));
    $this->session_key = $result['session_key'];
    return $result;
  }

  /* UTILITY FUNCTIONS */

  function call_method($method, $params) {
    $this->last_call_success = true;
    $this->last_error = null;

    $xml = $this->post_request($method, $params);
	
	$parser = xml_parser_create();
	xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,1);
	xml_parse_into_struct($parser, $xml, $vals);
	xml_parser_free($parser);
	
    $result = $this->convert_simplexml_to_array($vals); 
    if ($this->debug) {
      // output the raw xml and its corresponding php object, for debugging:
      print '<div style="width: 100%; clear: both;">';
      print '<div style="float: left; overflow: auto; width: 350px;"><pre>'.htmlspecialchars($xml).'</pre></div>';
      print '<div style="float: left; overflow: auto; width: 350px;"><pre>'.print_r($result, true).'</pre></div>';
      print '</div>';
    }
    if (is_array($result) && isset($result['fb_error'])) {
      $this->last_call_success = false;
      $this->last_error = $result['fb_error'];

      if ($this->throw_errors) {
        //throw new Exception($result['fb_error']['msg'], $result['fb_error']['code']);
      }
    }
    return $result;
  }

  function post_request($method, $params) {
    $params['method'] = $method;
    $params['session_key'] = $this->session_key;
    $params['api_key'] = $this->api_key;
    $params['call_id'] = microtime(true);

    $post_params = array();
    foreach ($params as $key => $val) {
      if (is_array($val)) $val = implode(',', $val);
      $post_params[] = $key.'='.urlencode($val);
    }

    $secret = $this->secret;
    $post_params[] = 'sig='.$this->api_generate_sig($params, $secret);
    $post_string = implode('&', $post_params);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $this->server_addr);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $result = curl_exec($ch);
    curl_close($ch);
 
    return $result;
  }

  /**
   * Generate a signature for the API call.
   */
  function api_generate_sig($params_array, $secret) {
    $str = '';

    ksort($params_array);
    foreach ($params_array as $k=>$v) {
      if ($k != 'sig')
        $str .= "$k=$v";
    }
    $str .= $secret;

    return md5($str);
  }

  function convert_simplexml_to_array($sxml) {
    $arr = array();
    if ($sxml)
	{
        for ($i = 1; $i < count($sxml) - 1; $i++)
		{
		  $key = strtolower($sxml[$i]['tag']);
		  $value = $sxml[$i]['value'];
		  $arr[$key] = $value;
		}
    }
    return $arr;
  }
}
?>